package com.wzh.project.utils.file;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.util.LinkedList;
import java.util.List;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.tools.zip.ZipEntry;
import org.apache.tools.zip.ZipOutputStream;

import com.css.sword.kernel.base.exception.SwordBaseCheckedException;

/**
*@author : 汪振海
*@createdDate : Jan 2, 2020  7:57:43 PM
*@description : 文件工具类<br>
*
*/
public class FileUtils {
	
	private static final Logger logger = LogManager.getLogger(FileUtils.class);
	
	/**
	 * 创建文件
	 * @param path
	 * @param fileName
	 * @throws SwordBaseCheckedException
	 */
	public static void createFile(String path,String fileName) throws SwordBaseCheckedException{
		File filePath=new File(path);
		if (!filePath.exists()) {
			filePath.mkdirs();
		}
		File file=new File(fileName);
		if (file.exists()) {
			try {
				file.createNewFile();
			} catch (IOException e) {
				SwordBaseCheckedException swordException=new SwordBaseCheckedException("");
				swordException.setStackTrace(e.getStackTrace());
				swordException.setMessage("创建文件失败");
				throw swordException;
			}
		}
	}
	
	public static void createFile(File file) throws SwordBaseCheckedException{
		if (file.isDirectory()) {
			throw new SwordBaseCheckedException("该文件是一个文件夹,请传递文件路径");
		}
		if (!file.exists()) {
			try {
				file.createNewFile();
			} catch (IOException e) {
				SwordBaseCheckedException swordException=new SwordBaseCheckedException("");
				swordException.setStackTrace(e.getStackTrace());
				swordException.setMessage("创建文件失败");
				throw swordException;
			}
		}
	}
	
	/**
	 * 使用字符流输出文件内容
	 * @param fileName	文件名字
	 * @param content	输入内容
	 * @throws FileNotFoundException 
	 * @throws UnsupportedEncodingException 
	 */
	public static void outputWriterFile(String filePath,String fileName,String content) throws SwordBaseCheckedException {
		createFile(filePath, fileName);
		File file = new File(filePath,fileName);
		Writer writer = null;
		BufferedWriter bufferedWriter = null;
		try {
			writer = new OutputStreamWriter(new FileOutputStream(file, false), "UTF-8");
			bufferedWriter = new BufferedWriter(writer);
			bufferedWriter.write(content);
		} catch (Exception e) {
			SwordBaseCheckedException swordException=new SwordBaseCheckedException("");
			swordException.setStackTrace(e.getStackTrace());
			swordException.setMessage("向文件写入内容失败");
			throw swordException;
		}finally {
			try {
				bufferedWriter.flush();
				bufferedWriter.close();
				writer.close();
			} catch (IOException e) {
				//已经关闭了 不用处理
			}
		}
	}
	
	
	
	/**
	 * 将目录directory中的文件递归创建条目并写入到zip输出流中 只压缩dirList 中存在的文件夹
	 * 
	 * @Description 相关说明
	 * @param zos
	 * @param directory
	 * @throws SwordBaseCheckedException
	 * @Time 创建时间:2012-11-2下午05:05:54
	 * @history 修订历史（历次修订内容、修订人、修订时间等）
	 */
	public static void putZipEntry(ZipOutputStream zos, String directory,List<String> dirList) throws SwordBaseCheckedException {
		LinkedList<String> files = listFiles(directory,false,dirList);
		//进行去重
		if (files != null && !files.isEmpty()) {
			directory = new File(directory).getAbsolutePath();
			for (String name : files) {
				try {
					if (new File(name).isFile()) {
						putZipEntry(zos, name.substring(directory.length() + 1), new BufferedInputStream(new FileInputStream(name)), true);
					} else {
						createZipEntry(zos, name.substring(directory.length() + 1), true, true);
					}
				} catch (FileNotFoundException ex) {
					throw new SwordBaseCheckedException("UT-11005:源文件" + name + "已被删除", ex);
				}
			}
		}
	}
	
	/**
	 * 如果path的路径中在list里面，那么就进行添加
	 * 因为这个是专为自己开发的下一级别就为需要压缩的路径，
	 * @throws SwordBaseCheckedException
	 */
	public static boolean isNeedGet(String path,List<String> dirList) throws SwordBaseCheckedException{
		for (int i = 0; i < dirList.size(); i++) {
			if (path.endsWith(dirList.get(i))) {
				dirList.remove(i);
				return true;
			}
		}
		return false;
	}
	
	
	public static LinkedList<String> listFiles(String directory, boolean skipDir,List<String> dirList) throws SwordBaseCheckedException {
        LinkedList<String> files = new LinkedList<String>();
        File dir = new File(directory);

        if (!dir.exists()) {
            throw new SwordBaseCheckedException("UT-06001:目录" + directory + "不存在");
        }
        if (!dir.isDirectory()) {
            throw new SwordBaseCheckedException("UT-06002:" + directory + "不是目录");
        }

        findSubFile(new File(directory), files, skipDir,dirList,false);
        if (!skipDir) {
            files.removeFirst();
        }
        return files;
    }
	
	/**
	 * 
	 * @param f
	 * @param files
	 * @param skipDir
	 * @param isFlag
	 * @throws SwordBaseCheckedException
	 */
	private static void findSubFile(File f, LinkedList<String> files, boolean skipDir,List<String> dirList,boolean isFlag) throws SwordBaseCheckedException {
        if (!f.exists()) {
            throw new SwordBaseCheckedException("UT-06003:文件或目录" + f + "不存在或已经删除");
        }

        if (f.isFile()) {
            files.add(f.getAbsolutePath());
        } else if (f.isDirectory()) {
            if (!skipDir) {
                files.add(f.getAbsolutePath());
            }
            for (File subFile : f.listFiles()) {
            	//在这一级别去重
            	if (isFlag||isNeedGet(subFile.getAbsolutePath(), dirList)) {
            		findSubFile(subFile, files, skipDir,dirList,true);
				}
            }
        } else {
            // 获取其它类型的对象
        }
    }
	
	/**
	 * zip输出流中添加一个条目, 并从输入流中读取数据写入到新条目entryName
	 * 
	 * @Description 相关说明
	 * @param zos
	 * @param entryName
	 * @param is
	 * @param autoCloseInputStream
	 *            结束后是否关闭输入流is
	 * @throws SwordBaseCheckedException
	 * @Time 创建时间:2012-11-2下午04:50:54
	 * @history 修订历史（历次修订内容、修订人、修订时间等）
	 */
	public static void putZipEntry(ZipOutputStream zos, String entryName, InputStream is, boolean autoCloseInputStream)
			throws SwordBaseCheckedException {
		byte[] buf = new byte[8192];
		int length = -1;

		// 增加ZIP文件条目
		createZipEntry(zos, entryName, false, false);

		// 写入当前条目的数据
		try {
			logger.info("开始压缩" + entryName + "条目");
			while ((length = is.read(buf)) != -1) {
				try {
					zos.write(buf, 0, length);
				} catch (IOException ex) {
					throw new SwordBaseCheckedException("UT-11002:向ZIP文件条目" + entryName + "写入数据时发生错误", ex);
				}
			}
			zos.closeEntry();
			logger.info(entryName + "条目压缩完成!");
		} catch (IOException ex) {
			throw new SwordBaseCheckedException("UT-11003:从输入流读取数据写入到ZIP流时发生错误，ZIP文件条目:" + entryName, ex);
		} finally {
			if (autoCloseInputStream) {
				try {
					is.close();
				} catch (IOException ex) {
					throw new SwordBaseCheckedException("UT-11004:关闭ZIP文件条目" + entryName + "数据输入流时发生错误", ex);
				}
			}
		}
	}
	
	/**
	 * 创建一个压缩文件条目
	 * @param zos
	 * @param entryName
	 * @param isDirectory
	 * @param autoClose
	 * @throws SwordBaseCheckedException
	 */
	public static void createZipEntry(ZipOutputStream zos, String entryName, boolean isDirectory, boolean autoClose)
			throws SwordBaseCheckedException {
		if (isDirectory && !entryName.endsWith("/")) {
			entryName += "/";
		}
		try {
			ZipEntry entry = new ZipEntry(entryName);
			entry.setComment(entryName);
			entry.setUnixMode(755);
			zos.putNextEntry(entry);
			if (isDirectory || autoClose) {
				zos.closeEntry();
			}
		} catch (IOException ex) {
			throw new SwordBaseCheckedException("UT-11001:增加ZIP文件条目" + entryName + "时发生错误", ex);
		}

	}
	
	
	/**
	 * @param file null、一个文件、一个文件目录、
	 * <pre>
	 * fileToByte(null)=null
	 * fileToByte(file)=null file>2G
	 * fileToByte(文件目录)=null
	 * fileToByte(file)=byte[]
	 * </pre>
	 * @return null(文件不存在,null,文件目录,文件太大>2G) byte[](转换成功)
	 */
	public static byte[] fileToByte(File file) {
		byte[] buffer = null;
		if (file == null) {
			throw new SwordBaseCheckedException("文件不存在");
		}
		if (file.isDirectory())
			throw new SwordBaseCheckedException("文件是一个文件夹");
		if (file.length() > Integer.MAX_VALUE)
			throw new SwordBaseCheckedException("文件太大");
		if (!file.exists()) {
			throw new SwordBaseCheckedException("文件不存在");
		}
		InputStream inputStream = null;
		BufferedInputStream bufferedInputStream = null;
		//输出流，经过内存的流的内容都会存储在此中
		ByteArrayOutputStream bos = null;
		
		if (file.isFile()) {
			int length = 4096;//涉及到操作系统4k对齐 扇区所以进行4K读取
			try {
				inputStream = new FileInputStream(file);
				bufferedInputStream = new BufferedInputStream(inputStream);
				bos = new ByteArrayOutputStream(length);
				buffer = new byte[length];
				int l=-1;
				/*
				 * 添加(length <= filelength - off) ? length : filelength - off)的比较
				 * 为了防止读到最后buffer 剩余的长度没有4096 装不下那么多会导致读取不了IndexOutOfBoundsException()
				 * 当filelength - off=0时表示文件读取完毕但是read内部认为是其他线程占用io导致堵塞并不会认为文件读取完毕
				 * 所以要添加上filelength - off>0
				 */
				while ((l = bufferedInputStream.read(buffer)) != -1) {
					bos.write(buffer, 0, l);
				}
			}
			catch (Exception e) {
			}
			finally {
				closeInputStream(bufferedInputStream);
				closeInputStream(inputStream);
			}
		}
		return bos.toByteArray();
	}
	
	/**
	 * byte[] 转为file
	 * @param bytes
	 * @param filePath
	 * @param fileName
	 */
	 public static void getFileByBytes(byte[] bytes, String filePath, String fileName) {
	        BufferedOutputStream bos = null;
	        FileOutputStream fos = null;
	        File file = null;
	        try {
	        	createFile(filePath, fileName);
	            file = new File(filePath , fileName);
	            fos = new FileOutputStream(file);
	            bos = new BufferedOutputStream(fos);
	            bos.write(bytes);
	        } catch (Exception e) {
	            throw new SwordBaseCheckedException("byte[]生成文件:"+filePath+fileName+"失败",e);
	        } finally {
                closeOutputStream(bos);
                closeOutputStream(fos);
	        }
	}
	 
	 public static void main(String[] args) {
		 File file=new File("C:\\Users\\wzh\\Downloads","XSD.zip");
		 byte[] bs = fileToByte(file);
		 getFileByBytes(bs, "D:\\java", "wzh.zip");
	}
	
	/**
	 * close inoutstream
	 * @param inputStream null or the inputstream's child
	 */
	private static void closeInputStream(InputStream inputStream) {
		if (inputStream == null)
			return;
		try {
			inputStream.close();
			System.out.println("关闭输入流成功");
		}
		catch (Exception e) {
		}
	}
	/**
	 * close outputStream
	 * @param outputStream null or the outputStream child
	 */
	private static void closeOutputStream(OutputStream outputStream) {
		if (outputStream == null)
			return;
		try {
			outputStream.flush();
			outputStream.close();
			System.out.println("关闭输出流成功");
		}
		catch (Exception e) {
		}
	}

}
